import 'dart:convert';
import 'dart:io';

import 'package:backend/injection_container.dart' as di;
import 'package:backend/router.dart';
import 'package:backend.analytics/domain/use_cases/run_amplitude_api_event_sending/use_case.dart';
import 'package:backend.analytics/injection_container.dart' as analytics_di;
import 'package:backend.auth/domain/use_cases/run_expired_email_verification_code_deletion/use_case.dart';
import 'package:backend.auth/domain/use_cases/run_expired_refresh_token_deletion/use_case.dart';
import 'package:backend.auth/injection_container.dart' as auth_di;
import 'package:backend.core/core/domain/config/config.dart';
import 'package:backend.core/core/infrastructure/middlewares/log_requests.dart';
import 'package:backend.core/injection_container.dart' as core_di;
import 'package:backend.core/logger/log_level.dart';
import 'package:backend.core/logger/logger.dart';
import 'package:backend.purchases/domain/use_cases/run_app_store_api_purchased_item_sync/use_case.dart';
import 'package:backend.purchases/domain/use_cases/run_google_play_api_purchased_item_acknowledgement/use_case.dart';
import 'package:backend.purchases/domain/use_cases/run_google_play_api_purchased_item_sync/use_case.dart';
import 'package:backend.purchases/domain/use_cases/run_web_purchased_subscription_renewal/use_case.dart';
import 'package:backend.purchases/domain/use_cases/run_yookassa_api_payment_sync/use_case.dart';
import 'package:backend.purchases/injection_container.dart' as purchases_di;
import 'package:backend.user_profile/domain/use_cases/run_expired_password_reset_token_deletion/use_case.dart';
import 'package:backend.user_profile/injection_container.dart' as user_profile_di;
import 'package:dotenv/dotenv.dart';
import 'package:googleapis_auth/auth_io.dart' as google_api;
import 'package:mongo_dart/mongo_dart.dart';
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart';
import 'package:shelf_router/shelf_router.dart';

void main(List<String> args) async {
  final env = DotEnv(includePlatformEnvironment: true);

  const envFileName = '.env';
  final envFileExists = await File.fromUri(Uri.parse(envFileName)).exists();

  if (envFileExists) {
    env.load([
      envFileName,
    ]);
  }

  final logLevelString = env['LOG_LEVEL']!;
  late final LogLevel logLevel;

  if (logLevelString == 'verbose') {
    logLevel = LogLevel.verbose;
  } else if (logLevelString == 'debug') {
    logLevel = LogLevel.debug;
  } else if (logLevelString == 'info') {
    logLevel = LogLevel.info;
  } else if (logLevelString == 'warning') {
    logLevel = LogLevel.warning;
  } else if (logLevelString == 'error') {
    logLevel = LogLevel.error;
  } else if (logLevelString == 'wtf') {
    logLevel = LogLevel.wtf;
  } else if (logLevelString == 'nothing') {
    logLevel = LogLevel.nothing;
  }

  Logger.logLevel = logLevel;

  final environmentString = env['ENVIRONMENT']!;
  late final Environment environment;

  if (environmentString == 'dev') {
    environment = Environment.dev;
  } else if (environmentString == 'stage') {
    environment = Environment.stage;
  } else if (environmentString == 'prod') {
    environment = Environment.prod;
  }

  final apiVersion = env['API_VERSION']!;
  final port = int.parse(env['PORT']!);
  final mongodbHost = env['MONGODB_HOST']!;
  final mongodbPort = int.parse(env['MONGODB_PORT']!);
  final mongodbDbName = env['MONGODB_DB_NAME']!;
  final apiAccessTokenSecretKey = env['API_ACCESS_TOKEN_SECRET_KEY']!;
  final noreplyEmailBoxPassword = env['NOREPLY_EMAIL_BOX_PASSWORD']!;
  final yookassaMerchantId = env['YOOKASSA_MERCHANT_ID']!;
  final yookassaSecretKey = env['YOOKASSA_SECRET_KEY']!;
  final appStoreSharedSecret = env['APP_STORE_SHARED_SECRET']!;
  final appStoreApiKeyId = env['APP_STORE_API_KEY_ID']!;
  final userEmailSendingEnablementString = env['USER_EMAIL_SENDING_ENABLED']!;
  final amplitudeApiKey = env['AMPLITUDE_API_KEY']!;

  late final bool userEmailSendingEnabled;

  if (userEmailSendingEnablementString == 'true') {
    userEmailSendingEnabled = true;
  } else if (userEmailSendingEnablementString == 'false') {
    userEmailSendingEnabled = false;
  }

  final googleServiceAccountFile = File.fromUri(Uri.parse('config/google_service_account.json'));
  final googleServiceAccountString = await googleServiceAccountFile.readAsString();
  final googleServiceAccountJson = jsonDecode(googleServiceAccountString);
  final googleServiceAccountCredentials =
      google_api.ServiceAccountCredentials.fromJson(googleServiceAccountJson);

  Config().init(
    environment: environment,
    mongodbHost: mongodbHost,
    mongodbPort: mongodbPort,
    mongodbDbName: mongodbDbName,
    apiAccessTokenSecretKey: apiAccessTokenSecretKey,
    noreplyEmailBoxPassword: noreplyEmailBoxPassword,
    userEmailSendingEnabled: userEmailSendingEnabled,
    yookassaMerchantId: yookassaMerchantId,
    yookassaSecretKey: yookassaSecretKey,
    googleServiceAccountCredentials: googleServiceAccountCredentials,
    appStoreSharedSecret: appStoreSharedSecret,
    appStoreApiKeyId: appStoreApiKeyId,
    amplitudeApiKey: amplitudeApiKey,
  );

  await di.init();

  final db = core_di.sl<Db>();

  while (true) {
    try {
      await db.open();
      break;
    } catch (_) {
      await Future.delayed(const Duration(seconds: 1));
    }
  }

  // analytics

  analytics_di.sl<RunAmplitudeApiEventSending>()();

  // auth

  auth_di.sl<RunExpiredEmailVerificationCodeDeletion>()();
  auth_di.sl<RunExpiredRefreshTokenDeletion>()();

  // purchases

  purchases_di.sl<RunAppStoreApiPurchasedItemSync>()();
  purchases_di.sl<RunGooglePlayApiPurchasedItemAcknowledgement>()();
  purchases_di.sl<RunGooglePlayApiPurchasedItemSync>()();
  purchases_di.sl<RunWebPurchasedSubscriptionRenewal>()();
  purchases_di.sl<RunYookassaApiPaymentSync>()();

  // user profile

  user_profile_di.sl<RunExpiredPasswordResetTokenDeletion>()();

  //

  final _logRequests = core_di.sl<LogRequests>();

  final rootRouter = Router();
  rootRouter.mount('/$apiVersion/', router);

  final handler = const Pipeline().addMiddleware(_logRequests()).addHandler(rootRouter);

  final service = await serve(handler, InternetAddress.anyIPv4, port);

  Logger().i('Server started on port ${service.port}');
}
